#include "types.h"
#include "params.h"
#include "util.h"
#include "memory.h"
#include "lock.h"
#include "kfunc.h"
#include "pipe.h"
#include "file.h"
#include "kalloc.h"
#include "proc.h"
#include "cons.h"
#include "kobj.h"

struct {
    struct ko kos [NR_MAXKO];
    int map [NR_MAXKO];
    struct sleeplk lk;
} kopool;

int
pipe (int fds) {
    struct proc * p;
    struct ko * o;
    p = myproc ();
    o = allocko ();
    creat_pipeko (o);
    int i, j;
    for (i = 0; i < NR_MAXOPEN; i++) {
    if (p->fdtab[i] == 0) {
    for (j = (i + 1) % NR_MAXOPEN; j < NR_MAXOPEN; j++) {
        if (p->fdtab[j] == 0)
            goto found;
    }}}
    freeko (o);
    // error:
    return -1;
    found:
    p->fdtab[i] = o;
    p->fdtab[j] = o;
    return 0;
}


int
dup (int ufd) {
    struct proc *p;
    p = myproc ();
    if (p->fdtab[ufd] == 0)
        goto error;
    int i;
    for (i = 0; i < NR_MAXOPEN; i++) {
        if (p->fdtab[i] == 0)
            goto found;
    }
    error:
    return -1;
    found:
    p->fdtab[i] = p->fdtab[ufd];
    return i;
}


int
close (int ufd) {
    struct proc * p;
    struct ko * o;
    p = myproc ();
    if (p->fdtab[ufd] == 0)
        goto error;
    o = p->fdtab[ufd];
    o->refs--;
    if (o->refs == 0)
        freeko (o);
    p->fdtab[ufd] = 0;
    return 0;
    error:
    return -1;
}


int
open (char * name) {
    struct proc * p;
    struct ko * o;
    p = myproc ();
    o = allocko ();
    if (o == 0)
        goto error;
    int i;
    for (i = 0; i < NR_MAXOPEN; i++) {
        if (p->fdtab[i] == 0)
            goto found;
    }
    goto error;
    found:
    if (creat_fileko (name, o) != 0)
        goto error;
    o->refs++;
    p->fdtab[i] = o;
    return i;
    error:
    return -1;
}

// read 0 for eof
int
read (int ufd, char * udest, int rdsz) {
    struct proc * p;
    struct ko * o;
    p = myproc ();
    if (p->fdtab[ufd] == 0)
        goto error;
    void * kbuf = kalloc ();
    o = p->fdtab[ufd];
    int r, cnt, rr;
    cnt = 0;
    while (rdsz) {
        r = rdsz < PGSZ? rdsz : PGSZ;
        //sleep_lock (&o->lk);
        rr = o->read (o, kbuf, r);
        //sleep_unlock (&o->lk);
        vmwrite (p->pgtb, kbuf,(unsigned int)udest, r);
        rdsz -= rr;
        cnt += rr;
        if (r != rr) break;
    }
    kfree (kbuf);
    return cnt;
    error:
    return -1;
}


// write 0 for eof
int
write (int ufd, char * usrc, int wrsz) {
    struct proc * p;
    struct ko * o;
    int w, cnt, ww;
    void * kbuf = kalloc ();
    p = myproc ();
    // if (p->fdtab[ufd] == 0)
    //     goto error;
    w = wrsz < PGSZ? wrsz : PGSZ;
    vmread (p->pgtb, (unsigned int) usrc, kbuf, w);
    printk ("%s",kbuf);
    panic ("write");
    o = p->fdtab[ufd];
    cnt = 0;
    while (wrsz) {
        w = wrsz < PGSZ? wrsz : PGSZ;
        vmread (p->pgtb, (unsigned int) usrc, kbuf, w);
        printk (kbuf);
        // panic ("write");
        ww = o->write (o, kbuf, w);
        //sleep_unlock (&o->lk);
        wrsz -= ww;
        cnt += ww;
        if (w != ww)
            break;
    }
    kfree (kbuf);
    return cnt;
    //error:
    return -1;
}


void
koallocinit () {
    bzero (&kopool, sizeof (kopool));
}

struct ko *
allocko () {
    int i;
    struct ko * ret;
    sleep_lock (&kopool.lk);
    for (i = 0; i < NR_MAXKO; i++) {
        if (kopool.map[i] == 0) {
            kopool.map[i] = 1;
            ret = &kopool.kos[i];
            ret->id = i;
            goto found;
        }
    }
    sleep_unlock (&kopool.lk);
    return 0; // NULL
    found:
    sleep_unlock (&kopool.lk);
    return ret;
}

int
freeko (struct ko * o) {
    o->freeinside(o->ctx);
    bzero (o,sizeof (struct ko));
    kopool.map[o->id] = 0;
    return 0;
}

int
creat_pipeko (struct ko * o) {
    void * pipe = alloc_pipe ();
    initko (o, read_pipeko, write_pipeko,free_pipe, pipe, "PIPE", KO_MODE_RW, 
            KO_TYPE_PIPE);
    return 0;
}

int
creat_consko (struct ko * o) {
    void * cons = getcons ();
    initko (o, read_consko, write_consko, 0 ,cons, "CONS", KO_MODE_RW, 
            KO_TYPE_CONS); // NOTE:CONS不会被关闭
    return 0;
}

int
creat_fileko (char * path , struct ko * o) {
    void * file = allocfile (path);
    initko (o, read_fileko, write_fileko, freefile ,file, path, KO_MODE_RW,
            KO_TYPE_FILE);
    return 0;
}

int
initko (struct ko* o, int(*read)(struct ko*,void*,unsigned int), 
int(*write)(struct ko*,void*,unsigned int), int(*frinsd)(void*),void * ctx, char * name,
enum ko_mode mode ,enum ko_type type) {
    bzero (o, sizeof (struct ko));
    o->freeinside = frinsd;
    o->read = read;
    o->write = write;
    o->ctx = ctx;
    o->mode = mode;
    o->type = type;
    o->refs = 0;
    strncpy (&o->name, name, MAX_NAMESZ-1);
    o->magic = BISCUIT_MAGIC;
    return 0;
}